package com.emeraldparser.symbol;

import static com.emeraldparser.util.Functions2.memoized;

import com.emeraldparser.util.Automaton;
import com.emeraldparser.util.Automatons;
import com.emeraldparser.util.HashCodes;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;

public class NestedSymbolClass implements SymbolClass {

	private final SymbolClass parentSymbolClass;
	private final SymbolClass childSymbolClass;
	private final transient int hashCode;

	public NestedSymbolClass(SymbolClass parentSymbolClass, SymbolClass childSymbolClass) {
		this.parentSymbolClass = Preconditions.checkNotNull(parentSymbolClass, "Parent symbol class must not be null");
		this.childSymbolClass = Preconditions.checkNotNull(childSymbolClass, "Child symbol class must not be null");
		this.hashCode = HashCodes.classSpecificHashCode(NestedSymbolClass.class, parentSymbolClass, childSymbolClass);
	}

	public SymbolClass getParentSymbolClass() {
		return parentSymbolClass;
	}

	public SymbolClass getChildSymbolClass() {
		return childSymbolClass;
	}

	@Override
	public Automaton<? extends State, ? extends SymbolClass> toAutomaton() {
		return Automatons.transform(
				childSymbolClass.toAutomaton(),
				memoized(
						new Function<State, State>() {
							@Override public State apply(State childState) {
								return new NestedSymbolClassState(NestedSymbolClass.this, childState);
							}
						}
					),
				memoized(
						new Function<SymbolClass, SymbolClass>() {
							@Override public SymbolClass apply(SymbolClass childSymbolClass) {
								return new NestedSymbolClass(NestedSymbolClass.this, childSymbolClass);
							}
						}
					)
			);
	}

	@Override
	public int hashCode() {
		return hashCode;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		
		if (obj == null) {
			return false;
		}
		
		if (getClass() != obj.getClass()) {
			return false;
		}
		
		NestedSymbolClass other = (NestedSymbolClass) obj;
		
		return Objects.equal(parentSymbolClass, other.parentSymbolClass)
		    && Objects.equal(childSymbolClass, other.childSymbolClass);
	}

	@Override
	public <I, O> O accept(SymbolClassVisitor<I, O> visitor, I input) {
		// TODO Auto-generated method stub
		return null;
	}

}
